PIC16F877A(PICマイコン)

PIC16F877A(PICマイコン)

TEditorMX(テキストエディタ)をご活用頂ければと思いまして、PIC16F877A(PICマイコン)です。
Z80ボード同様にI/O入出力とシリアル通信を行おうと思います。
PICマイコンはI/OやA/Dコンバータ、シリアル通信、タイマなどが内蔵されているため、 その範囲内で使用する分には比較的簡単な回路構成で済んでしまいます。


私(管理人)はPICを使うのは今回が全くの初めてでしてRISC CPU(PIC)のアセンブラでプログラムするのも初めてです。


PICでプログラムを作成するにはどうするのが早道かと考えて、 できればC言語でサクッと終わらせたかったのですがそれではPICのことを全く理解できずに何もできない...、 というわけで仕方なくアセンブリ言語を理解することから始めることにしました。
PICはRISC CPUなので命令が簡単(単純)なものしかなく、ニーモニックもちょっと分かりづらかったので「どうしよう...」 と思いながらPIC16F877Aのニーモニック表を見ていたらちょっとした規則に気付きまして「これなら簡単に理解できそう」 ということになりました。で、その規則ですが、非常に簡単で ニーモニックの先頭に機能が付いてその後に操作対象を示す記号が付くことが多いということです。

例えば、

MOV移動
ADD加算
SUB減算

などの後に続くのは、

LW定数 → ファイルレジスタ
WFW → ファイルレジスタ
Fファイルレジスタ → W or F(ファイルレジスタ)

の3種類です。

そう分かると少ない命令が更に少なくなって、ニーモニックも比較的簡単に理解できそうです。
...というわけですので頑張って少しだけ覚えてくださいね。

今回作成するプログラムで使用する命令は、

移動命令MOVxx
分岐命令BTFSS, BTFSC
ジャンプ命令GOTO
ビットセット、クリア命令BSF, BCF
クリア命令CLRx
スワップ命令SWAPF
サブルーチン命令CALL, RETURN
割り込み復帰命令RETFIE
疑似命令ORG, EQU, BANKSEL, END

くらいです。
その他の疑似命令もありますがとりあえず「おまじない」とお考え頂ければ大丈夫だと思います。



もう1つ理解しなければならないことがあります。 それはメモリ構成です。 PIC16F877Aにはプログラムを書き込むEEPROM(8KW), RAM&I/O(総称としてレジスタまたはファイルレジスタと呼ばれているようです)(4バンク), スタック(8段) です。プログラムを書き込むためのEEPROMは8KWありますが、2KW×4ページという構成になっています。 小さいプログラム(2KW以下)であれば「ページ」を気にする必要はありません (ちなみに2KWの「KW」は「キロワード」の略でPIC16F877Aの命令長は14ビットで1ワードです)。 RAM&I/Oはファイルレジスタと呼ばれているようで全部で255バイト×4バンクしかありません。 スタックについては8段しかなく、それ以上サブルーチン(割り込みも含む)を呼び出してしまうと正常に動作しません。 ファイルレジスタの詳細については解説書かマニュアルをご参照ください。 マニュアル(英語)はインターネットで検索すると出てきます。



今回はアセンブリ言語でプログラムを作成しますので下記の2つのどちらかをダウンロードしてインストールしてください (インターネットで検索すればすぐに出てくると思いますし、インストール方法も掲載しているホームページがあると思います。 ちなみに私は両方ともデフォルトの設定でインストールしてしまいました)。

(1)MPLAB XIDE (2019年6月現在、v5.20が無償で利用できます)に含まれているmpasmx.exeとインクルードファイル

(2)gputils (GNU PIC Utilities)




インストールしたら(1)の場合にはmpasmx.exeのあるフォルダにパスを通します。 mpasmx.exeは私(管理人)の場合には下記のフォルダにインストールされていました。

C:\Program Files (x86)\Microchip\MPLABX\v5.20\mpasmx

パスを通せばコマンドプロンプト(cmd.exe)でmpasmxと入力すると起動できます。

このページのサンプルプログラムをアセンブルするためのコマンドは、

mpasmx main.asm /p16f877a /q

とすればmain.hexというファイル(インテルヘキサファイル)が作成されてPICに焼くことができます。




インストールしたら(2)の場合にはgpasm.exeにパスが通っていると思いますのでコマンドプロンプト(cmd.exe)でgpasmと入力すれば起動すると思います。

このページのサンプルプログラムをアセンブルするためのコマンドは、

gpasm main.asm

とすればmain.hexというファイル(インテルヘキサファイル)が作成されてPICに焼くことができます。


以上でアセンブラに関してはOKです。



ページのTOPへ




続いてハードウェア(自作する基板)です。

必要な(主な)部品は以下のようになります。

品名型番など数量購入先
ユニバーサル基板CPU-133アマゾン
CPUPIC16F877A秋月電子通商
ICソケット40極秋月電子通商
RS232CインターフェースICICL3232CPZ秋月電子通商
トランジスタアレイTD62083APG秋月電子通商
セラミック発振子10MHz秋月電子通商
LED秋月電子通商
抵抗(1/4W)10KΩ秋月電子通商
抵抗(1/4W)100KΩ秋月電子通商
抵抗(1/4W)4.7KΩ秋月電子通商
抵抗(1/4W)470Ω秋月電子通商
積層セラミックコンデンサ0.1μF秋月電子通商
積層セラミックコンデンサ1μF秋月電子通商
ダイオード1S1588相当品秋月電子通商
コネクタXG4M-1030-T楽天
コネクタDSUB 9pin メスアマゾン


PCと接続するためのRS-232Cクロスケーブル 1本
ケーブルは購入しても良いですが、自作してしまっても良いのではないかと思います。
今回使用するクロスケーブルの結線図は下図の通りです(Z80で使用したものと同じものです)。

rs232c結線図

その他必要なものは手持ちであったものを使用しました。


ページのTOPへ




(1)スイッチの入力とLEDへの出力

まずは簡単なディップスイッチの入力とLEDへの出力です。
回路は下図のようになります。
io回路画像

制作した基板(I/Oプログラム動作中)は下図のようになりました。
(下図には次に使用するRS-232CインターフェースICも取り付けてあります)。
基板画像

PORTDでスイッチの入力を受けてPORTBへ出力します。
アセンブリ言語プログラムは以下のようになりました(io.zip)。

	LIST		p=16f877a
	#INCLUDE	p16f877a.inc
;	__CONFIG	_HS_OSC & _CP_OFF & _PWRTE_OFF & _WDT_OFF & _LVP_OFF
	__CONFIG	_FOSC_HS & _WDTE_OFF & _PWRTE_OFF & _BOREN_OFF & _LVP_OFF & _CPD_OFF & _WRT_OFF & _CP_OFF

;-------------------------------------------------------------
;初期化
;-------------------------------------------------------------

; BANK0選択
	BCF	STATUS, RP0
	BCF	STATUS, RP1

;念のためI/Oポート出力を0に
	CLRF	PORTA
	CLRF	PORTB
	CLRF	PORTC
	CLRF	PORTD
	CLRF	PORTE

;BANK1を選択
	BSF	STATUS, RP0

;PORTDを入力に設定
	MOVLW	0xff
	MOVWF	TRISD

;PORTD以外をすべて出力に設定
	CLRF	TRISA
	CLRF	TRISB
	CLRF	TRISC
	CLRF	TRISE

;BANK0を選択
	BCF	STATUS, RP0

;-------------------------------------------------------------
;メインループ
;-------------------------------------------------------------
main:

;PORTDを読み込み、そのままPORTBに出力
	MOVF	PORTD, w
	MOVWF	PORTB
	GOTO	main

	END

上記プログラムでは使用していないポートはすべて出力に設定していて、出力はLowレベル(0)にしています。 これは未使用の入力ポートをオープンにしたままだとCPU内部で大電流が流れる可能性があって最悪の場合、 PICが壊れてしまうことがあるために行っています。 そのため、テスターやプローブなどを接続するときは気を付けて下さいね。

基板に電源を入れ、ディップスイッチをON/OFFするとそれに合わせてLEDが消灯/点灯します。



ページのTOPへ




(2)シリアル通信

次はシリアル通信プログラムです。
受信には割り込みを使用します。
送信にも割り込みを使用するつもりでいたのですが、 (たぶんハードバグではないかと思いますが)上手く動作させることができませんでしたので割り込みは使用していません。 まぁPIC16F877Aの場合、RAMの容量も少ないので長い文の通信はしない方が良いかと思いますので良しとします。

今回のプログラムは割り込みで受信した文字をオウム返しで送信するだけのものです。 多分このままでは機能的に不十分だと思いますので改造してご利用下さいね。 回路図は下図のようになります。
シリアル通信回路画像

制作した基板は(1)スイッチの入力とLEDへの出力で使用した基板とまったく同じものです。
(下図はI/Oプログラム動作中です)
基板画像

アセンブリ言語プログラムは以下のようになりました(serial.zip)。

;-------------------------------------------------------------
; PIC16F877A 10MHz用
; RS-232C 送受信サンプルプログラム
;	受信した文字をオウム返しで送信する
;	受信のみ割り込みを使用
;
; アセンブル方法
;	mpasmx main.asm /p16f877a /q
;	gpasm main.asm
;
; 確認している不具合が1点あります。
; (1)電源投入時にゴミデータが1バイトくらい相手側に送信されてしまいます。
;   PIC16F887Aの問題(ハードバグ)なのかもしれません。
;   回避方法として簡単なのは「起動メッセージ」を出力してそれ以降のデータを有効とする方法が考えられます。
;
; その他
;  送信側も割込みを使用することを試みたのですが、上手くいきませんでした。
;  多分ハードバグが原因だと考えています。
;-------------------------------------------------------------

;	ERRORLEVEL	-302		;mpasmx.exe で Message 302 を出力しないように指定
	ERRORLEVEL	-1302		;gpasm.exe で Message 1302 を出力しないように指定

	LIST		p=16f877a
	#INCLUDE	p16f877a.inc
;	__CONFIG	_HS_OSC & _CP_OFF & _PWRTE_OFF & _WDT_OFF & _LVP_OFF
	__CONFIG	_FOSC_HS & _WDTE_OFF & _PWRTE_ON & _BOREN_ON & _LVP_OFF & _CPD_OFF & _WRT_OFF & _CP_OFF



;レジスタ(バンク切り替えに影響しないレジスタへ割り当て)
RXTMP		EQU	0x7c
PCLATH_TEMP	EQU	0x7d
W_TEMP		EQU	0x7e
STATUS_TEMP	EQU	0x7f



	ORG	0x0
	GOTO	init		;初期化部へジャンプ


;-------------------------------------------------------------
; 割り込みルーチン
;  PIC16F877A(だけじゃないかも)は0x04番地が唯一の割り込み開始
;  アドレスです
;-------------------------------------------------------------
	ORG	0x4	;割り込み開始アドレス
;	BCF	INTCON, GIE		; di

	MOVWF	W_TEMP			; WやSTATUSレジスタ等を退避
	SWAPF	STATUS, W		;
	CLRF	STATUS			;
	MOVWF	STATUS_TEMP		;
	MOVF	PCLATH, W		;
	MOVWF	PCLATH_TEMP		;
	CLRF	PCLATH			;

;割り込み要因チェック
	BTFSS	PIR1, RCIF
	GOTO	INT_NEXT1

;	BCF	PIR1, RCIF	;割り込み要因フラグクリア

;1バイト受信
	MOVF	RCREG,W		;受信した文字を取り込み
;	MOVWF	PORTB		;そのままPORTBへ出力
	MOVWF	RXTMP		;バッファへ保存
	GOTO	INT_NEXT1


INT_NEXT1:


INT_END:	; 割り込みルーチン終了処理
;	BSF	INTCON, GIE		; ei

	MOVF	PCLATH_TEMP, W		; WやSTATUSレジスタ等を復帰
	MOVWF	PCLATH			;
	SWAPF	STATUS_TEMP, W		;
	MOVWF	STATUS			;
	SWAPF	W_TEMP, F		;
	SWAPF	W_TEMP, W		;

	RETFIE		; 割り込みルーチンからリターン



;-------------------------------------------------------------
; 初期化
;-------------------------------------------------------------

init:
; BANK0選択
	BANKSEL	PORTA
;念のためI/Oポート出力を0に
	CLRF	PORTA
	CLRF	PORTB
	CLRF	PORTC
	CLRF	PORTD
	CLRF	PORTE

;BANK1を選択
	BANKSEL	TRISD

;PORTDを入力に設定
	MOVLW	0xff
	MOVWF	TRISD

;PORTC,PORTD以外をすべて出力に設定
	CLRF	TRISA
	CLRF	TRISB
	CLRF	TRISE

;RS232C設定
;RCSTA
	BANKSEL	RCSTA
	MOVLW	B'10010000'
	MOVWF	RCSTA

;TXSTA
	BANKSEL	TXSTA
	MOVLW	B'00100110'
;	MOVLW	B'00100100'
	MOVWF	TXSTA
;SPBRG
	BANKSEL	SPBRG
	MOVLW	D'64'
	MOVWF	SPBRG

;BANK1を選択
	BANKSEL	TRISC
;PORTCのbit7は入力、それ以外は出力に設定
	MOVLW	B'10000000'
	MOVWF	TRISC

;割り込み設定
;PIE1
	BANKSEL	PIE1
	MOVLW	B'00100000'		;とりあえず受信割り込みだけ設定
;	MOVLW	B'00010000'		;送信割り込みだけ設定
;	MOVLW	B'00110000'		;送受信割り込みはこっち
	MOVWF	PIE1
;INTCON
	BANKSEL	INTCON
	MOVLW	B'11000000'		;割り込み許可(bit7は全体の割り込み許可)
	MOVWF	INTCON

;BANK0を選択
	BANKSEL	PORTA		;PORTA は BANK0



;-------------------------------------------------------------
;メインループfdsfgdsggds
;-------------------------------------------------------------
main:
	MOVF	RXTMP, W
	BTFSC	STATUS, Z	;RXTMPが0のときはmainに戻る
	GOTO	main

	CLRF	RXTMP		;受信データを0に(重複送信を防ぐため)
	CALL	txchar		;Wの値(文字)を送信
	GOTO	main


;-------------------------------------------------------------
; 1文字送信
;-------------------------------------------------------------
txchar:
;	MOVWF	PORTB		;そのままPORTBへ出力
	BTFSS	PIR1, TXIF
	GOTO	$-1

	MOVWF	TXREG

	RETURN


	END




プログラムについて少しだけ解説します。

シリアル通信を行うための設定は、

TXSTA送信とボーレート関連の設定
RCSTA受信他の設定
SPBRGボーレート設定

を設定すればOKです。


また割り込みを使用する場合には更に、

PIE1個別に割り込み許可
INTCON全体的な割り込み許可

を設定すればOKです。


本プログラムでの通信仕様は、

ボーレート9600bps
ビット数8
ストップビット1
パリティなし
同期/非同期非同期
フロー制御なし

です。


割り込みについてですが、
PIC16F877Aの割り込み開始アドレスは0x4固定ですべての割り込み共通です。 そのため割り込みルーチン内で何の割り込みで呼び出されたのかをチェックする必要があります。

割り込みルーチンでは、使用するレジスタを退避・復帰する必要がありますが、そのやり方は下記のようになります。

PCLATH_TEMP	EQU	0x7d
W_TEMP		EQU	0x7e
STATUS_TEMP	EQU	0x7f

    ・
    ・
    ・

	ORG	0x4			; 割り込みルーチン開始アドレス

;退避処理
	MOVWF	W_TEMP			; WやSTATUSレジスタ等を退避
	SWAPF	STATUS, W		;
	CLRF	STATUS			;
	MOVWF	STATUS_TEMP		;
	MOVF	PCLATH, W		;
	MOVWF	PCLATH_TEMP		;
	CLRF	PCLATH			;

;
; ここに割り込み処理を記述します
;

;退避処理
	MOVF	PCLATH_TEMP, W		; WやSTATUSレジスタ等を復帰
	MOVWF	PCLATH			;
	SWAPF	STATUS_TEMP, W		;
	MOVWF	STATUS			;
	SWAPF	W_TEMP, F		;
	SWAPF	W_TEMP, W		;

	RETFIE		; 割り込みルーチンからリターン

スタック用のメモリがたくさんあって退避・復帰が簡単なCPUでしたらもっとスマートなやり方ができるのですがRISC CPUだからなのでしょうか?... いや多分PICだからなのでしょうが、ちょっとスマートではないような気がします...が、このやり方はマニュアルに記述されていますので正しいやり方です。

受信割り込み処理では受信したデータをRXTMP(ファイルレジスタ)に保存するだけで終わりです。
メインループ側ではRXTMPに0以外のデータ(値)が入っているときにはその文字を送信します。
送信したら(正確には送信データとしてWレジスタにコピーしたら...ですが)RXTMPに0を代入して次のデータを待ちます。

このプログラムでは正常な受信データには0は含まれていないものとしています。 通常はデバッグ作業を容易にするためバイナリデータはテキストデータに変換して送受信するのが一般的です。 そのためこれは「仕様」ということになります。

基板が出来上がって、シリアル通信プログラムをPIC16F877Aに焼いて基板に取り付けたらPCと接続します。
あとはPC上でターミナルソフトを起動し、基板の電源を入れればキーボードから入力した文字がターミナルソフト上に表示されると思います。

ちょっとした不具合があるのですが、基板に電源を入れたときにゴミデータが1バイトくらい出力されてしまうようです。 ゴミデータが出力されないようにいろいろ対策を試みたのですがどうしようもない感じなのです。 なので、実際に使用する場合には起動メッセージなどを出力するようにしてそれ以前に出力されたデータはゴミデータとして処理しないようにするなどの工夫が必要になると思われます。

そうそう、書き忘れましたが基板に供給する電源はDC5Vにして下さい。

今回、私(管理人)が使用したターミナルソフトはフリーソフトの Multi Port Terminal ver1.00 (金澤ソフト設計さん)を利用させて頂きました。 Vectorさんよりダウンロードさせて頂きました。


最後に、このページからダウンロードできるソースプログラムに関してはフリーウェアとします。 よろしければご自由にお使い下さい。



ページのTOPへ

メニュー